package com.gitee.wsl.android.ext.device

import android.Manifest
import android.app.ActivityManager
import android.app.ActivityManager.RunningAppProcessInfo
import android.app.Application
import android.app.usage.UsageStats
import android.app.usage.UsageStatsManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.provider.Settings
import android.text.TextUtils
import androidx.annotation.RequiresPermission
import com.gitee.wsl.android.ext.ApplicationInit.application
import com.gitee.wsl.ext.base.ifValue
import com.gitee.wsl.jvm.ext.base.match
import timber.log.Timber
import java.io.BufferedReader
import java.io.DataOutputStream
import java.io.File
import java.io.FileReader
import java.io.InputStream
import java.io.InputStreamReader
import java.util.Collections


object ShellExt {

    object Command{
        const val Logcat="logcat -d *:E -t 300 "
    }

    fun setupEnv(env:HashMap<String, String>,useSu:Boolean=false,command:DataOutputStream.()->Unit={}):Process {
        val pb =  ProcessBuilder(useSu.ifValue("su",""));
        val process = pb.start();
         // The su binary prohibits the inheritance of environment variables.
         // Even with --preserve-environment the environment gets messed up.
         // We therefore start a root shell, and set all the environment variables manually.
        val suOut  =  DataOutputStream(process.getOutputStream())
        env.forEach { (t, u) ->
              suOut.writeBytes(String.format("export %s=\"%s\"\n", t, u))
        }
        command(suOut)
        suOut.flush()
        return process
    }

    /**
     * 执行命令
     *
     * @param command 字符串数组命令
     * @return 执行结果
     */
    @JvmStatic
    fun execProcessBuilderCommand(vararg command: String): CommandResult {
        var process: Process? = null
        var msg: StringBuilder? = null
        var resultCode = -1
        try {
            process = ProcessBuilder(*command).redirectErrorStream(true).start()
            msg = StringBuilder(inputStream2StringBuilder(process.inputStream))
            resultCode = process.waitFor()
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        } finally {
            process?.destroy()
        }
        return CommandResult(resultCode, msg?.toString())
    }

    /**
     * 执行命令
     *
     * @param shell 字符串命令
     * @return 执行结果
     */
    @JvmStatic
    fun execRuntimeCommand(shell: String): CommandResult {
        var process: Process? = null
        var msg: StringBuilder? = null
        var resultCode = -1
        try {
            process = Runtime.getRuntime().exec(shell)
            msg = StringBuilder(inputStream2StringBuilder(process.inputStream))
            msg.append(inputStream2StringBuilder(process.errorStream))
            resultCode = process.waitFor()
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        } finally {
            process?.destroy()
        }
        return CommandResult(resultCode, msg?.toString())
    }


    /************ 系统内核kernel信息读取 ************/

    /************ 系统内核kernel信息读取  */
    /**
     * Linux version 3.14.29 (eric@bogon) (gcc version 4.9.2 20140904 (prerelease) (cro
     * sstool-NG linaro-1.13.1-4.9-2014.09 - Linaro GCC 4.9-2014.09) ) #11 SMP PREEMPT
     * Tue Jul 16 12:15:26 CST 2019
     *
     * 解析为 3.14.29
     *
     * @return
     */
    fun getLinuxKernelVersion(): String {
        var versionStr=""
        try {
            val result: CommandResult = execProcessBuilderCommand("cat", "/proc/version")
            result.resultString?.match("version\\s(.*?(?=\\s))".toPattern())?.let {
                versionStr=it
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return versionStr
    }

    /**
     * 命令结果
     */
    class CommandResult @JvmOverloads constructor(var resultCode: Int = -1, var resultString: String?)

    /**
     * 信息流
     *
     * @param stream 输入流
     * @return StringBuilder
     * @throws Exception
     */
    @JvmStatic
    @Throws(java.lang.Exception::class)
    fun inputStream2StringBuilder(stream: InputStream): java.lang.StringBuilder {
        val result = java.lang.StringBuilder()
        val reader = BufferedReader(InputStreamReader(stream))
        var line: String?
        while (reader.readLine().also { line = it } != null) {
            result.append(line)
        }
        reader.close()
        return result
    }

    /**
     * 销毁自身进程
     */
    fun kill() {
        kill(android.os.Process.myPid())
    }

    /**
     * 销毁进程
     * @param pid 进程 id
     */
    fun kill(pid: Int) {
        android.os.Process.killProcess(pid)
    }

    /**
     * 获取自身进程 id
     * @return 自身进程 id
     */
    fun myPid(): Int {
        return android.os.Process.myPid()
    }

    /**
     * 判断是否当前进程
     * @return `true` yes, `false` no
     */
    fun isCurProcess(): Boolean {
        val packageName: String = application.packageName
        try {
            return packageName == getCurProcessName()
        } catch (e: java.lang.Exception) {
            Timber.d( e, "isCurProcess")
        }
        return false
    }

    /**
     * 获取当前进程名
     * @return 进程名
     */
    fun getCurProcessName(): String? {
        try {
            // 获取自身进程 id
            val pid = android.os.Process.myPid()
            // 判断全部运行中的进程
            val activityManager: ActivityManager = application.activityManager
            val lists = activityManager.runningAppProcesses
            for (appProcess in lists) {
                if (appProcess.pid == pid) {
                    return appProcess.processName
                }
            }
        } catch (e: java.lang.Exception) {
            Timber.d( e, "getCurProcessName")
        }
        return null
    }

    /**
     * 获取进程 id 对应的进程名
     * @param pid 进程 id
     * @return 进程名
     */
    fun getProcessName(pid: Int): String? {
        var br: BufferedReader? = null
        try {
            br = BufferedReader(FileReader("/proc/$pid/cmdline"))
            var processName = br.readLine()
            if (!TextUtils.isEmpty(processName)) {
                processName = processName.trim { it <= ' ' }
            }
            return processName
        } catch (throwable: Throwable) {
            Timber.d( throwable, "getProcessName")
        } finally {
            br?.close()
        }
        return null
    }

    /**
     * 根据包名获取进程 id
     * @param packageName 应用包名
     * @return 进程 id
     */
    fun getPid(packageName: String): Int {
        try {
            val activityManager: ActivityManager = application.activityManager
            val lists = activityManager.runningAppProcesses
            for (appProcess in lists) {
                if (packageName == appProcess.processName) {
                    return appProcess.pid
                }
            }
        } catch (e: java.lang.Exception) {
            Timber.d( e, "getPid")
        }
        return 0
    }

    /**
     * 根据进程 id 获取进程信息
     * @param pid 进程 id
     * @return 进程信息
     */
    fun getRunningAppProcessInfo(pid: Int): RunningAppProcessInfo? {
        try {
            val activityManager: ActivityManager = application.activityManager
            val lists = activityManager.runningAppProcesses
            for (appProcess in lists) {
                if (appProcess.pid == pid) {
                    return appProcess
                }
            }
        } catch (e: java.lang.Exception) {
            Timber.d( e, "getRunningAppProcessInfo")
        }
        return null
    }

    /**
     * 根据包名获取进程信息
     * @param packageName 应用包名
     * @return 进程信息
     */
    fun getRunningAppProcessInfo(packageName: String): RunningAppProcessInfo? {
        try {
            val activityManager: ActivityManager = application.activityManager
            val lists = activityManager.runningAppProcesses
            for (appProcess in lists) {
                if (packageName == appProcess.processName) {
                    return appProcess
                }
            }
        } catch (e: java.lang.Exception) {
            Timber.d( e, "getRunningAppProcessInfo")
        }
        return null
    }

    // =

    // =
    /**
     * 获取前台线程包名
     * @return 前台应用包名
     */
    fun getForegroundProcessName(): String? {
        try {
            val activityManager: ActivityManager = application.activityManager
            val lists = activityManager.runningAppProcesses
            for (appProcess in lists) {
                if (appProcess.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
                    return appProcess.processName
                }
            }
        } catch (e: java.lang.Exception) {
            Timber.d( e, "getForegroundProcessName")
        }
        // SDK 大于 21 时
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
            val packageManager: PackageManager = application.packageManager
            try {
                val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
                val lists = packageManager.queryIntentActivities(
                    intent, PackageManager.MATCH_DEFAULT_ONLY
                )
                // 无权限
                if (lists.size == 0) return null
                val usageStatsManager: UsageStatsManager = application.usageStatsManager
                var listUsageStats: List<UsageStats>? = null
                if (usageStatsManager != null) {
                    val endTime = System.currentTimeMillis()
                    val beginTime = endTime - 86400000 * 7
                    listUsageStats = usageStatsManager.queryUsageStats(
                        UsageStatsManager.INTERVAL_BEST, beginTime, endTime
                    )
                }
                if (listUsageStats == null || listUsageStats.isEmpty()) return null
                var recentStats: UsageStats? = null
                for (usageStats in listUsageStats) {
                    if (recentStats == null || usageStats.lastTimeUsed > recentStats.lastTimeUsed) {
                        recentStats = usageStats
                    }
                }
                return recentStats?.packageName
            } catch (e: java.lang.Exception) {
                Timber.d( e, "getForegroundProcessName")
            }
        }
        return null
    }

    /**
     * 获取后台服务进程
     * @return 后台服务进程
     */
    fun getAllBackgroundProcesses(): Set<String> {
        try {
            val set: MutableSet<String> = HashSet()
            val activityManager: ActivityManager = application.activityManager
            val lists = activityManager.runningAppProcesses
            for (appProcess in lists) {
                Collections.addAll(set, *appProcess.pkgList)
            }
            return set
        } catch (e: java.lang.Exception) {
            Timber.d( e, "getAllBackgroundProcesses")
        }
        return emptySet()
    }

    /**
     * 杀死所有的后台服务进程
     * @return 被暂时杀死的服务集合
     */
    @RequiresPermission(Manifest.permission.KILL_BACKGROUND_PROCESSES)
    fun killAllBackgroundProcesses(): Set<String> {
        try {
            val set: MutableSet<String> = HashSet()
            val activityManager: ActivityManager = application.activityManager
            var lists = activityManager.runningAppProcesses
            for (appProcess in lists) {
                for (packageName in appProcess.pkgList) {
                    activityManager.killBackgroundProcesses(packageName)
                    set.add(packageName)
                }
            }
            lists = activityManager.runningAppProcesses
            for (appProcess in lists) {
                for (packageName in appProcess.pkgList) {
                    set.remove(packageName)
                }
            }
            return set
        } catch (e: java.lang.Exception) {
            Timber.d( e, "killAllBackgroundProcesses")
        }
        return emptySet()
    }

    /**
     * 杀死后台服务进程
     * @param packageName 应用包名
     * @return `true` success, `false` fail
     */
    @RequiresPermission(Manifest.permission.KILL_BACKGROUND_PROCESSES)
    fun killBackgroundProcesses(packageName: String): Boolean {
        try {
            val activityManager: ActivityManager = application.activityManager
            var lists = activityManager.runningAppProcesses
            if (lists == null || lists.size == 0) return true
            for (appProcess in lists) {
                if (listOf(*appProcess.pkgList).contains(packageName)) {
                    activityManager.killBackgroundProcesses(packageName)
                }
            }
            lists = activityManager.runningAppProcesses
            if (lists == null || lists.size == 0) return true
            for (appProcess in lists) {
                if (listOf(*appProcess.pkgList).contains(packageName)) {
                    return false
                }
            }
            return true
        } catch (e: java.lang.Exception) {
            Timber.d( e, "killBackgroundProcesses")
        }
        return false
    }

    /**
     * Return whether ADB is enabled.
     * 判断设备 ADB 是否可用
     *
     * @return `true`: yes<br></br>`false`: no
     */

    fun isAdbEnabled(): Boolean {
        return Settings.Secure.getInt(
            application.contentResolver,
            Settings.Global.ADB_ENABLED, 0
        ) > 0
    }

    /**
     * 检查是否拥有root权限
     * 执行shell adb 命令很多需要root权限
     *
     * @return
     */
    fun isCheckRoot(): Boolean {
        var f: File? = null
        val kSuSearchPaths = arrayOf(
            "/system/bin/", "/system/xbin/", "/system/sbin/", "/sbin" +
                    "/", "/vendor/bin/"
        )
        try {
            for (i in kSuSearchPaths.indices) {
                f = File(kSuSearchPaths[i] + "su")
                if (f.exists()) {
                    Timber.d("find su in : " + kSuSearchPaths[i])
                    return true
                }
            }
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
        return false
    }

}

val globalUsageStatsManager:UsageStatsManager by lazy { application.usageStatsManager }

val Context.usageStatsManager:UsageStatsManager
    get() = application.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager

fun Context.currentProcessName(): String {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        val processName = Application.getProcessName()
        if (processName != null && processName.isNotBlank()) {
            return processName
        }
    }

    val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
    val infoList = activityManager.runningAppProcesses
    return infoList?.find { it.pid == android.os.Process.myPid() }?.processName ?: packageName
}

fun Context.currentSimpleProcessName(): String {
    val name = currentProcessName()
    return name.split(":").getOrElse(1) { "main" }
}